home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / quicktime / wired movies and sprites / qtwiredsprites / qtwiredsprites.c < prev    next >
Encoding:
Text File  |  2000-06-23  |  27.1 KB  |  618 lines

  1. //////////
  2. //
  3. //    File:        QTWiredSprites.c
  4. //
  5. //    Contains:    QuickTime wired sprites support for QuickTime movies.
  6. //
  7. //    Written by:    Sean Allen
  8. //    Revised by:    Chris Flick and Tim Monroe
  9. //                Based (heavily!) on the existing MakeActionSpriteMovie.c code written by Sean Allen.
  10. //
  11. //    Copyright:    © 1997-1998 by Apple Computer, Inc., all rights reserved.
  12. //
  13. //    Change History (most recent first):
  14. //
  15. //       <3>         09/30/98    rtm        tweaked call to AddMovieResource to create single-fork movies
  16. //       <2>         03/26/98    rtm        made fixes for Windows compiles
  17. //       <1>         03/25/98    rtm        first file; integrated existing code with shell framework
  18. //       
  19. //
  20. //    This sample code creates a wired sprite movie containing one sprite track. The sprite track contains
  21. //    six sprites: two penguins and four buttons.
  22. //    
  23. //    The four buttons are initially invisible. When the mouse enters (or "rolls over") a button, it appears.
  24. //    When the mouse is clicked inside a button, its image changes to its "pressed" image. When the mouse
  25. //    is released, its image changes back to its "unpressed" image. If the mouse is released inside the button, 
  26. //    an action is triggered. The buttons perform the actions of go to beginning of movie, step backward,
  27. //    step forward, and go to end of movie.
  28. //    
  29. //    The first penguin shows all of the buttons when the mouse enters it, and hides them when the mouse exits.
  30. //    The first penguin is the only sprite that has properties that are overriden by the override sprite samples.
  31. //    These samples override its matrix (in order to move it) and its image index (in order to make it "waddle").
  32. //    
  33. //    When the mouse is clicked on the second penguin, it changes its image index to it's "eyes closed" image.
  34. //    When the mouse is released, it changes back to its normal image. This makes it appear to blink when clicked on.
  35. //    When the mouse is released over the penguin, several actions are triggered. Both penguins' graphics states are 
  36. //    toggled between copyMode and blendMode, and the movie's rate is toggled between zero and one.
  37. //    
  38. //    The second penguin moves once per second. This occurs whether the movie's rate is currently zero or one,
  39. //    because it is being triggered by a gated idle event. When the penguin receives the idle event, it changes
  40. //    its matrix using an action which uses min, max, delta, and wraparound options.
  41. //
  42. //    The movie's looping mode is set to palindrome by a frame-loaded action.
  43. //
  44. //    So, our general strategy is as follows (though perhaps not in the order listed):
  45. //
  46. //        (1) Create a new movie file with a single sprite track.
  47. //        (2) Assign the "no controller" movie controller to the movie.
  48. //        (3) Set the sprite track's background color, idle event frequency, and hasActions properties.
  49. //        (4) Convert our PICT resources to animation codec images with transparency.
  50. //        (5) Create a key frame sample containing six sprites and all of their shared images.
  51. //        (6) Assign the sprites their initial property values.
  52. //        (7)    Create a frameLoaded event for the key frame.
  53. //        (8)    Create some override samples that override the matrix and image index properties of
  54. //            the first penguin sprite.
  55. //
  56. //    NOTES:
  57. //        
  58. //    *** (1) ***
  59. //    There are event types other that mouse related events (for instance, Idle and FrameLoaded).
  60. //    Idle events are independent of the movie's rate, and they can be gated so they are send at most
  61. //    every n ticks. In our sample movie, the second penguin moves when the movie's rate is zero,
  62. //    and moves only once per second because of the value of the sprite track's idleEventFrequencey property.
  63. //        
  64. //    *** (2) ***
  65. //    Multiple actions may be executed in response to a single event. In our sample movie, rolling over
  66. //    the first penguin shows and hides four different buttons.
  67. //        
  68. //    *** (3) ***
  69. //    Actions may target any sprite or track in the movie. In our sample movie, clicking on one penguin
  70. //    changes the graphics mode of the other.
  71. //        
  72. //    *** (4) ***
  73. //    Conditional and looping control structures are supported. In our sample movie, the second penguin
  74. //    uses the "case statement" action.
  75. //        
  76. //    *** (5) ***
  77. //    Sprite track variables that have not been set have a default value of zero. (The second penguin's
  78. //    conditional code relies on this.)
  79. //        
  80. //    *** (6) ***
  81. //    Wired sprites were previously known as "action sprites". Don't let the names of some of the utility
  82. //    functions confuse you. We'll try to update the source code as time permits.
  83. //        
  84. //    *** (7) ***
  85. //    Penguins don't fly, but I hear they totally shred halfpipes on snowboards.
  86. //
  87. //////////
  88.  
  89.  
  90. // header files
  91. #include "QTWiredSprites.h"
  92.  
  93.  
  94. //////////
  95. //
  96. // QTWired_CreateWiredSpritesMovie
  97. // Create a QuickTime movie containing a wired sprites track.
  98. //
  99. //////////
  100.  
  101. OSErr QTWired_CreateWiredSpritesMovie (void)
  102. {
  103.     short                    myResRefNum = 0;
  104.     short                    myResID = movieInDataForkResID;
  105.     Movie                    myMovie = NULL;
  106.     Track                    myTrack;
  107.     Media                    myMedia;
  108.     StandardFileReply        myReply;
  109.     QTAtomContainer            mySample = NULL;
  110.     QTAtomContainer            myActions = NULL;
  111.     QTAtomContainer            myBeginButton, myPrevButton, myNextButton, myEndButton;
  112.     QTAtomContainer            myPenguinOne, myPenguinTwo, myPenguinOneOverride;
  113.     QTAtomContainer            myBeginActionButton, myPrevActionButton, myNextActionButton, myEndActionButton;
  114.     QTAtomContainer            myPenguinOneAction, myPenguinTwoAction;
  115.     RGBColor                myKeyColor;
  116.     Point                    myLocation;
  117.     short                    isVisible, myLayer, myIndex, myID, i, myDelta;
  118.     Boolean                    hasActions;
  119.     long                    myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile;
  120.     OSType                    myType = FOUR_CHAR_CODE('none');
  121.     UInt32                    myFrequency;
  122.     QTAtom                    myEventAtom;
  123.     long                    myLoopingFlags;
  124.     ModifierTrackGraphicsModeRecord        myGraphicsMode;
  125.     OSErr                    myErr = noErr;
  126.  
  127.     //////////
  128.     //
  129.     // create a new movie file and set its controller type
  130.     //
  131.     //////////
  132.  
  133.     // ask the user for the name of the new movie file
  134.     StandardPutFile("\pSprite movie file name:", "\pWiredSprite.mov", &myReply);
  135.     if (!myReply.sfGood)
  136.         goto bail;
  137.  
  138.     // create a movie file for the destination movie
  139.     myErr = CreateMovieFile(&myReply.sfFile, FOUR_CHAR_CODE('TVOD'), smSystemScript, myFlags, &myResRefNum, &myMovie);
  140.     if (myErr != noErr)
  141.         goto bail;
  142.     
  143.     // select the "no controller" movie controller
  144.     myType = EndianU32_NtoB(myType);
  145.     SetUserDataItem(GetMovieUserData(myMovie), &myType, sizeof(myType), kUserDataMovieControllerType, 1);
  146.     
  147.     //////////
  148.     //
  149.     // create the sprite track and media
  150.     //
  151.     //////////
  152.     
  153.     myTrack = NewMovieTrack(myMovie, ((long)kSpriteTrackWidth << 16), ((long)kSpriteTrackHeight << 16), kNoVolume);
  154.     myMedia = NewTrackMedia(myTrack, SpriteMediaType, kSpriteMediaTimeScale, NULL, 0);
  155.  
  156.     //////////
  157.     //
  158.     // create a key frame sample containing six sprites and all of their shared images
  159.     //
  160.     //////////
  161.  
  162.     // create a new, empty key frame sample
  163.     myErr = QTNewAtomContainer(&mySample);
  164.     if (myErr != noErr)
  165.         goto bail;
  166.  
  167.     myKeyColor.red = 0xffff;                        // white
  168.     myKeyColor.green = 0xffff;
  169.     myKeyColor.blue = 0xffff;
  170.  
  171.     // add images to the key frame sample
  172.     AddPICTImageToKeyFrameSample(mySample, kGoToBeginningButtonUp, &myKeyColor, kGoToBeginningButtonUpIndex, NULL, NULL);
  173.     AddPICTImageToKeyFrameSample(mySample, kGoToBeginningButtonDown, &myKeyColor, kGoToBeginningButtonDownIndex, NULL, NULL);
  174.     AddPICTImageToKeyFrameSample(mySample, kGoToEndButtonUp, &myKeyColor, kGoToEndButtonUpIndex, NULL, NULL);
  175.     AddPICTImageToKeyFrameSample(mySample, kGoToEndButtonDown, &myKeyColor, kGoToEndButtonDownIndex, NULL, NULL);
  176.     AddPICTImageToKeyFrameSample(mySample, kGoToPrevButtonUp, &myKeyColor, kGoToPrevButtonUpIndex, NULL, NULL);
  177.     AddPICTImageToKeyFrameSample(mySample, kGoToPrevButtonDown, &myKeyColor, kGoToPrevButtonDownIndex, NULL, NULL);
  178.     AddPICTImageToKeyFrameSample(mySample, kGoToNextButtonUp, &myKeyColor, kGoToNextButtonUpIndex, NULL, NULL);
  179.     AddPICTImageToKeyFrameSample(mySample, kGoToNextButtonDown, &myKeyColor, kGoToNextButtonDownIndex, NULL, NULL);
  180.     AddPICTImageToKeyFrameSample(mySample, kPenguinForward, &myKeyColor, kPenguinForwardIndex, NULL, NULL);
  181.     AddPICTImageToKeyFrameSample(mySample, kPenguinLeft, &myKeyColor, kPenguinLeftIndex, NULL, NULL);
  182.     AddPICTImageToKeyFrameSample(mySample, kPenguinRight, &myKeyColor, kPenguinRightIndex, NULL, NULL);
  183.     AddPICTImageToKeyFrameSample(mySample, kPenguinClosed, &myKeyColor, kPenguinClosedIndex, NULL, NULL);
  184.  
  185.     for (myIndex = kPenguinDownRightCycleStartIndex, myID = kWalkDownRightCycleStart; myIndex <= kPenguinDownRightCycleEndIndex; myIndex++, myID++)
  186.         AddPICTImageToKeyFrameSample(mySample, myID, &myKeyColor, myIndex, NULL, NULL);
  187.     
  188.     // assign group IDs to the images
  189.     AssignImageGroupIDsToKeyFrame(mySample);
  190.     
  191.     //////////
  192.     //
  193.     // add samples to the sprite track's media
  194.     //
  195.     //////////
  196.     
  197.     BeginMediaEdits(myMedia);
  198.  
  199.     // go to beginning button with no actions
  200.     myErr = QTNewAtomContainer(&myBeginButton);
  201.     if (myErr != noErr)
  202.         goto bail;
  203.     myLocation.h    = (1 * kSpriteTrackWidth / 8) - (kStartEndButtonWidth / 2);
  204.     myLocation.v    = (4 * kSpriteTrackHeight / 5) - (kStartEndButtonHeight / 2);
  205.     isVisible        = false;
  206.     myLayer            = 1;
  207.     myIndex            = kGoToBeginningButtonUpIndex;
  208.     myErr = SetSpriteData(myBeginButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, myActions);
  209.     if (myErr != noErr)
  210.         goto bail;
  211.  
  212.     // go to previous button with no actions
  213.     myErr = QTNewAtomContainer(&myPrevButton);
  214.     if (myErr != noErr)
  215.         goto bail;
  216.     myLocation.h     = (3 * kSpriteTrackWidth / 8) - (kNextPrevButtonWidth / 2);
  217.     myLocation.v    = (4 * kSpriteTrackHeight / 5) - (kStartEndButtonHeight / 2);
  218.     isVisible        = false;
  219.     myLayer            = 1;
  220.     myIndex            = kGoToPrevButtonUpIndex;
  221.     myErr = SetSpriteData(myPrevButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, myActions);
  222.     if (myErr != noErr)
  223.         goto bail;
  224.  
  225.     // go to next button with no actions
  226.     myErr = QTNewAtomContainer(&myNextButton);
  227.     if (myErr != noErr)
  228.         goto bail;
  229.     myLocation.h     = (5 * kSpriteTrackWidth / 8) - (kNextPrevButtonWidth / 2);
  230.     myLocation.v    = (4 * kSpriteTrackHeight / 5) - (kStartEndButtonHeight / 2);
  231.     isVisible        = false;
  232.     myLayer            = 1;
  233.     myIndex            = kGoToNextButtonUpIndex;
  234.     myErr = SetSpriteData(myNextButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, myActions);
  235.     if (myErr != noErr)
  236.         goto bail;
  237.  
  238.     // go to end button with no actions
  239.     myErr = QTNewAtomContainer(&myEndButton);
  240.     if (myErr != noErr)
  241.         goto bail;
  242.     myLocation.h     = (7 * kSpriteTrackWidth / 8) - (kStartEndButtonWidth / 2);
  243.     myLocation.v    = (4 * kSpriteTrackHeight / 5) - (kStartEndButtonHeight / 2);
  244.     isVisible        = false;
  245.     myLayer            = 1;
  246.     myIndex            = kGoToEndButtonUpIndex;
  247.     myErr = SetSpriteData(myEndButton, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, myActions);
  248.     if (myErr != noErr)
  249.         goto bail;
  250.  
  251.     // first penguin sprite with no actions
  252.     myErr = QTNewAtomContainer(&myPenguinOne);
  253.     if (myErr != noErr)
  254.         goto bail;
  255.     myLocation.h     = (3 * kSpriteTrackWidth / 8) - (kPenguinWidth / 2);
  256.     myLocation.v     = (kSpriteTrackHeight / 4) - (kPenguinHeight / 2);
  257.     isVisible        = true;
  258.     myLayer            = 2;
  259.     myIndex            = kPenguinDownRightCycleStartIndex;
  260.     myGraphicsMode.graphicsMode = blend;
  261.     myGraphicsMode.opColor.red = myGraphicsMode.opColor.green = myGraphicsMode.opColor.blue = 0x8FFF;    // grey
  262.     myErr = SetSpriteData(myPenguinOne, &myLocation, &isVisible, &myLayer, &myIndex, &myGraphicsMode, NULL, myActions);
  263.     if (myErr != noErr)
  264.         goto bail;
  265.  
  266.     // second penguin sprite with no actions
  267.     myErr = QTNewAtomContainer(&myPenguinTwo);
  268.     if (myErr != noErr)
  269.         goto bail;
  270.     myLocation.h     = (5 * kSpriteTrackWidth / 8) - (kPenguinWidth / 2);
  271.     myLocation.v     = (kSpriteTrackHeight / 4) - (kPenguinHeight / 2);
  272.     isVisible        = true;
  273.     myLayer            = 3;
  274.     myIndex            = kPenguinForwardIndex;
  275.     myErr = SetSpriteData(myPenguinTwo, &myLocation, &isVisible, &myLayer, &myIndex, NULL, NULL, myActions);
  276.     if (myErr != noErr)
  277.         goto bail;
  278.  
  279.     //////////
  280.     //
  281.     // add actions to the six sprites
  282.     //
  283.     //////////
  284.  
  285.     // add go to beginning button
  286.     myErr = QTCopyAtom(myBeginButton, kParentAtomIsContainer, &myBeginActionButton);
  287.     if (myErr != noErr)
  288.         goto bail;
  289.  
  290.     AddSpriteSetImageIndexAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, kGoToBeginningButtonDownIndex, NULL);
  291.     AddSpriteSetImageIndexAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, kGoToBeginningButtonUpIndex, NULL);
  292.     AddMovieGoToBeginningAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton);
  293.     AddSpriteSetVisibleAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL, true, NULL);
  294.     AddSpriteSetVisibleAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL, false, NULL);
  295.     AddSpriteToSample(mySample, myBeginActionButton, kGoToBeginningSpriteID);
  296.     QTDisposeAtomContainer(myBeginActionButton);
  297.  
  298.     // add go to prev button
  299.     myErr = QTCopyAtom(myPrevButton, kParentAtomIsContainer, &myPrevActionButton);
  300.     if (myErr != noErr)
  301.         goto bail;
  302.  
  303.     AddSpriteSetImageIndexAction(myPrevActionButton, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, kGoToPrevButtonDownIndex, NULL);
  304.     AddSpriteSetImageIndexAction(myPrevActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, kGoToPrevButtonUpIndex, NULL);
  305.     AddMovieStepBackwardAction(myPrevActionButton, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton);
  306.     AddSpriteSetVisibleAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL, true, NULL);
  307.     AddSpriteSetVisibleAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL, false, NULL);
  308.     AddSpriteToSample(mySample, myPrevActionButton, kGoToPrevSpriteID);
  309.     QTDisposeAtomContainer(myPrevActionButton);
  310.  
  311.     // add go to next button
  312.     myErr = QTCopyAtom(myNextButton, kParentAtomIsContainer, &myNextActionButton);
  313.     if (myErr != noErr)
  314.         goto bail;
  315.  
  316.     AddSpriteSetImageIndexAction(myNextActionButton, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, kGoToNextButtonDownIndex, NULL);
  317.     AddSpriteSetImageIndexAction(myNextActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, kGoToNextButtonUpIndex, NULL);
  318.     AddMovieStepForwardAction(myNextActionButton, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton);
  319.     AddSpriteSetVisibleAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL, true, NULL);
  320.     AddSpriteSetVisibleAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL, false, NULL);
  321.     AddSpriteToSample(mySample, myNextActionButton, kGoToNextSpriteID);
  322.     QTDisposeAtomContainer(myNextActionButton);
  323.  
  324.     // add go to end button
  325.     myErr = QTCopyAtom(myEndButton, kParentAtomIsContainer, &myEndActionButton);
  326.     if (myErr != noErr)
  327.         goto bail;
  328.  
  329.     AddSpriteSetImageIndexAction(myEndActionButton, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, kGoToEndButtonDownIndex, NULL);
  330.     AddSpriteSetImageIndexAction(myEndActionButton, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, kGoToEndButtonUpIndex, NULL);
  331.     AddMovieGoToEndAction(myEndActionButton, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton);
  332.     AddSpriteSetVisibleAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL, true, NULL);
  333.     AddSpriteSetVisibleAction(myBeginActionButton, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL, false, NULL);
  334.     AddSpriteToSample(mySample, myEndActionButton, kGoToEndSpriteID);
  335.     QTDisposeAtomContainer(myEndActionButton);
  336.  
  337.     // add penguin one
  338.     myErr = QTCopyAtom(myPenguinOne, kParentAtomIsContainer, &myPenguinOneAction);
  339.     if (myErr != noErr)
  340.         goto bail;
  341.  
  342.     // show the buttons on mouse enter and hide them on mouse exit
  343.     AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, kTargetSpriteID, (void *)kGoToBeginningSpriteID, true, NULL);
  344.     AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, kTargetSpriteID, (void *)kGoToBeginningSpriteID, false, NULL);
  345.     AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, kTargetSpriteID, (void *)kGoToPrevSpriteID, true, NULL);
  346.     AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, kTargetSpriteID, (void *)kGoToPrevSpriteID, false, NULL);
  347.     AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, kTargetSpriteID, (void *)kGoToNextSpriteID, true, NULL);
  348.     AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, kTargetSpriteID, (void *)kGoToNextSpriteID, false, NULL);
  349.     AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, kTargetSpriteID, (void *)kGoToEndSpriteID, true, NULL);
  350.     AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, kTargetSpriteID, (void *)kGoToEndSpriteID, false, NULL);
  351.     AddSpriteToSample(mySample, myPenguinOneAction, kPenguinOneSpriteID);
  352.     QTDisposeAtomContainer(myPenguinOneAction);
  353.  
  354.     // add penguin two
  355.     myErr = QTCopyAtom(myPenguinTwo, kParentAtomIsContainer, &myPenguinTwoAction);
  356.     if (myErr != noErr)
  357.         goto bail;
  358.  
  359.     // blink when clicked on
  360.     AddSpriteSetImageIndexAction(myPenguinTwoAction, kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL, kPenguinClosedIndex, NULL);
  361.     AddSpriteSetImageIndexAction(myPenguinTwoAction, kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0, NULL, kPenguinForwardIndex, NULL);
  362.  
  363.     AddQTEventAtom(myPenguinTwoAction, kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton, &myEventAtom);
  364.  
  365.     // toggle the movie rate and both of the birds' graphics modes
  366.     QTWired_AddPenguinTwoConditionalActions(myPenguinTwoAction, myEventAtom);
  367.  
  368.     QTWired_AddWraparoundMatrixOnIdle(myPenguinTwoAction);
  369.  
  370.     AddSpriteToSample(mySample, myPenguinTwoAction, kPenguinTwoSpriteID);
  371.     QTDisposeAtomContainer(myPenguinTwoAction);
  372.     
  373.     // add an action for when the key frame is loaded, to set the movie's looping mode to palindrome;
  374.     // note that this will actually be triggered every time the key frame is reloaded,
  375.     // so if the operation was expensive we could use a conditional to test if we've already done it
  376.     myLoopingFlags = loopTimeBase | palindromeLoopTimeBase;
  377.     AddMovieSetLoopingFlagsAction(mySample, kParentAtomIsContainer, kQTEventFrameLoaded, myLoopingFlags);
  378.  
  379.     // add the key frame sample to the sprite track media
  380.     //
  381.     // to add the sample data in a compressed form, you would use a QuickTime DataCodec to perform the
  382.     // compression; replace the call to the utility AddSpriteSampleToMedia with a call to the utility
  383.     // AddCompressedSpriteSampleToMedia to do this
  384.     
  385.     AddSpriteSampleToMedia(myMedia, mySample, kSpriteMediaFrameDuration, true, NULL);    
  386.     //AddCompressedSpriteSampleToMedia(myMedia, mySample, kSpriteMediaFrameDuration, true, zlibDataCompressorSubType, NULL);
  387.  
  388.     //////////
  389.     //
  390.     // add a few override samples to move penguin one and change its image index
  391.     //
  392.     //////////
  393.  
  394.     // original penguin one location
  395.     myLocation.h     = (3 * kSpriteTrackWidth / 8) - (kPenguinWidth / 2);
  396.     myLocation.v     = (kSpriteTrackHeight / 4) - (kPenguinHeight / 2);
  397.  
  398.     myDelta = (kSpriteTrackHeight / 2) / kNumOverrideSamples;
  399.     myIndex = kPenguinDownRightCycleStartIndex;
  400.     
  401.     for (i = 1; i <= kNumOverrideSamples; i++) {
  402.         QTRemoveChildren(mySample, kParentAtomIsContainer);
  403.         QTNewAtomContainer(&myPenguinOneOverride);
  404.  
  405.         myLocation.h += myDelta;
  406.         myLocation.v += myDelta;
  407.         myIndex++;
  408.         if (myIndex > kPenguinDownRightCycleEndIndex)
  409.             myIndex = kPenguinDownRightCycleStartIndex;
  410.             
  411.         SetSpriteData(myPenguinOneOverride, &myLocation, NULL, NULL, &myIndex, NULL, NULL, NULL);
  412.         AddSpriteToSample(mySample, myPenguinOneOverride, kPenguinOneSpriteID);
  413.         AddSpriteSampleToMedia(myMedia, mySample, kSpriteMediaFrameDuration, false, NULL);    
  414.         QTDisposeAtomContainer(myPenguinOneOverride);
  415.     }
  416.  
  417.     EndMediaEdits(myMedia);
  418.     
  419.     // add the media to the track
  420.     InsertMediaIntoTrack(myTrack, 0, 0, GetMediaDuration(myMedia), fixed1);
  421.     
  422.     //////////
  423.     //
  424.     // set the sprite track properties
  425.     //
  426.     //////////
  427.     {
  428.         QTAtomContainer        myTrackProperties;
  429.         RGBColor            myBackgroundColor;
  430.         
  431.         // add a background color to the sprite track
  432.         myBackgroundColor.red = EndianU16_NtoB(0x8000);
  433.         myBackgroundColor.green = EndianU16_NtoB(0);
  434.         myBackgroundColor.blue = EndianU16_NtoB(0xffff);
  435.         
  436.         QTNewAtomContainer(&myTrackProperties);
  437.         QTInsertChild(myTrackProperties, 0, kSpriteTrackPropertyBackgroundColor, 1, 1, sizeof(RGBColor), &myBackgroundColor, NULL);
  438.  
  439.         // tell the movie controller that this sprite track has actions, Jackson
  440.         hasActions = true;
  441.         QTInsertChild(myTrackProperties, 0, kSpriteTrackPropertyHasActions, 1, 1, sizeof(hasActions), &hasActions, NULL);
  442.     
  443.         // tell the sprite track to generate QTIdleEvents
  444.         myFrequency = EndianU32_NtoB(60);
  445.         QTInsertChild(myTrackProperties, 0, kSpriteTrackPropertyQTIdleEventsFrequency, 1, 1, sizeof(myFrequency), &myFrequency, NULL);
  446.         myErr = SetMediaPropertyAtom(myMedia, myTrackProperties);
  447.         if (myErr != noErr)
  448.             goto bail;
  449.  
  450.         QTDisposeAtomContainer(myTrackProperties);
  451.     }
  452.     
  453.     //////////
  454.     //
  455.     // finish up
  456.     //
  457.     //////////
  458.     
  459.     // add the movie resource to the movie file
  460.     myErr = AddMovieResource(myMovie, myResRefNum, &myResID, myReply.sfFile.name);
  461.     
  462. bail:
  463.     if (mySample != NULL)
  464.         QTDisposeAtomContainer(mySample);
  465.  
  466.     if (myBeginButton != NULL)
  467.         QTDisposeAtomContainer(myBeginButton);    
  468.             
  469.     if (myPrevButton != NULL)
  470.         QTDisposeAtomContainer(myPrevButton);
  471.                 
  472.     if (myNextButton != NULL)
  473.         QTDisposeAtomContainer(myNextButton);
  474.                 
  475.     if (myEndButton != NULL)
  476.         QTDisposeAtomContainer(myEndButton);        
  477.         
  478.     if (myResRefNum != 0)
  479.         CloseMovieFile(myResRefNum);
  480.  
  481.     if (myMovie != NULL)
  482.         DisposeMovie(myMovie);
  483.         
  484.     return(myErr);
  485. }
  486.  
  487.  
  488. //////////
  489. //
  490. // QTWired_AddPenguinTwoConditionalActions
  491. // Add actions to the second penguin that transform him (her?) into a two state button
  492. // that plays or pauses the movie.
  493. //
  494. // We are relying on the fact that a "GetVariable" for a variable ID which has never been set
  495. // will return zero. If we needed a different default value, we could initialize it using the
  496. // frameLoaded event.
  497. //
  498. // A higher-level description of the logic is:
  499. // 
  500. //     On MouseUpInside
  501. //        If (GetVariable(DefaultTrack, 1) = 0)
  502. //           SetMovieRate(1)
  503. //           SetSpriteGraphicsMode(DefaultSprite, { blend, grey } )
  504. //           SetSpriteGraphicsMode(GetSpriteByID(DefaultTrack, 5), { ditherCopy, white } )
  505. //           SetVariable(DefaultTrack, 1, 1)
  506. //        ElseIf (GetVariable(DefaultTrack, 1) = 1)
  507. //           SetMovieRate(0)
  508. //           SetSpriteGraphicsMode(DefaultSprite, { ditherCopy, white })
  509. //           SetSpriteGraphicsMode(GetSpriteByID(DefaultTrack, 5), { blend, grey })
  510. //           SetVariable(DefaultTrack, 1, 0)
  511. //        Endif
  512. //     End
  513. // 
  514. //////////
  515.  
  516. OSErr QTWired_AddPenguinTwoConditionalActions (QTAtomContainer theContainer, QTAtom theEventAtom)
  517. {
  518.     QTAtom                                myNewActionAtom, myNewParamAtom, myConditionalAtom;
  519.     QTAtom                                myExpressionAtom, myOperatorAtom, myActionListAtom;
  520.     short                                myParamIndex, myConditionIndex, myOperandIndex;
  521.     float                                myConstantValue;
  522.     QTAtomID                            myVariableID;
  523.     ModifierTrackGraphicsModeRecord        myBlendMode, myCopyMode;
  524.     
  525.     myBlendMode.graphicsMode = blend;
  526.     myBlendMode.opColor.red = myBlendMode.opColor.green = myBlendMode.opColor.blue = 0x8fff;    // grey
  527.  
  528.     myCopyMode.graphicsMode = ditherCopy;
  529.     myCopyMode.opColor.red = myCopyMode.opColor.green = myCopyMode.opColor.blue = 0xffff;        // white
  530.  
  531.     AddActionAtom(theContainer, theEventAtom, kActionCase, &myNewActionAtom);
  532.     
  533.     myParamIndex = 1;
  534.     AddActionParameterAtom(theContainer, myNewActionAtom, myParamIndex, 0, NULL, &myNewParamAtom);
  535.  
  536.     // first condition
  537.     myConditionIndex = 1;
  538.     AddConditionalAtom(theContainer, myNewParamAtom, myConditionIndex, &myConditionalAtom);
  539.     AddExpressionContainerAtomType(theContainer, myConditionalAtom, &myExpressionAtom);
  540.     AddOperatorAtom(theContainer, myExpressionAtom, kOperatorEqualTo, &myOperatorAtom);
  541.  
  542.     myOperandIndex = 1;
  543.     myConstantValue = kButtonStateOne;
  544.     AddOperandAtom(theContainer, myOperatorAtom, kOperandConstant, myOperandIndex, NULL, myConstantValue);
  545.  
  546.     myOperandIndex = 2;
  547.     myVariableID = kPenguinStateVariableID;
  548.     AddVariableOperandAtom(theContainer, myOperatorAtom, myOperandIndex, 0, NULL, 0, myVariableID);
  549.  
  550.     AddActionListAtom(theContainer, myConditionalAtom, &myActionListAtom);
  551.     AddMovieSetRateAction(theContainer, myActionListAtom, 0, Long2Fix(1));
  552.     AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0, NULL, 0, 0, NULL, &myBlendMode, NULL);
  553.     AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0, NULL, 0, kTargetSpriteID, (void *)kPenguinOneSpriteID, &myCopyMode, NULL);
  554.     AddSpriteTrackSetVariableAction(theContainer, myActionListAtom, 0, kPenguinStateVariableID, kButtonStateTwo, 0, NULL, 0);
  555.                                        
  556.     // second condition
  557.     myConditionIndex = 2;
  558.     AddConditionalAtom(theContainer, myNewParamAtom, myConditionIndex, &myConditionalAtom);
  559.     AddExpressionContainerAtomType(theContainer, myConditionalAtom, &myExpressionAtom);
  560.     AddOperatorAtom(theContainer, myExpressionAtom, kOperatorEqualTo, &myOperatorAtom);
  561.  
  562.     myOperandIndex = 1;
  563.     myConstantValue = kButtonStateTwo;
  564.     AddOperandAtom(theContainer, myOperatorAtom, kOperandConstant, myOperandIndex, NULL, myConstantValue);
  565.  
  566.     myOperandIndex = 2;
  567.     myVariableID = kPenguinStateVariableID;
  568.     AddVariableOperandAtom(theContainer, myOperatorAtom, myOperandIndex, 0, NULL, 0, myVariableID);
  569.  
  570.     AddActionListAtom(theContainer, myConditionalAtom, &myActionListAtom);
  571.     AddMovieSetRateAction(theContainer, myActionListAtom, 0, Long2Fix(0));
  572.     AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0, NULL, 0, 0, NULL, &myCopyMode, NULL);
  573.     AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0, NULL, 0, kTargetSpriteID, (void *)kPenguinOneSpriteID, &myBlendMode, NULL);
  574.     AddSpriteTrackSetVariableAction(theContainer, myActionListAtom, 0, kPenguinStateVariableID, kButtonStateOne, 0, NULL, 0);
  575.  
  576.     return(noErr);
  577. }
  578.  
  579.  
  580. //////////
  581. //
  582. // QTWired_AddWraparoundMatrixOnIdle
  583. // Add beginning, end, and change matrices to the specified atom container.
  584. //
  585. //////////
  586.  
  587. OSErr QTWired_AddWraparoundMatrixOnIdle (QTAtomContainer theContainer)
  588. {
  589.     MatrixRecord     myMinMatrix, myMaxMatrix, myDeltaMatrix;
  590.     long            myFlags = kActionFlagActionIsDelta | kActionFlagParameterWrapsAround;
  591.     QTAtom            myActionAtom;
  592.     OSErr            myErr = noErr;
  593.     
  594.     myMinMatrix.matrix[0][0] = myMinMatrix.matrix[0][1] = myMinMatrix.matrix[0][2] = EndianS32_NtoB(0xffffffff);
  595.     myMinMatrix.matrix[1][0] = myMinMatrix.matrix[1][1] = myMinMatrix.matrix[1][2] = EndianS32_NtoB(0xffffffff);
  596.     myMinMatrix.matrix[2][0] = myMinMatrix.matrix[2][1] = myMinMatrix.matrix[2][2] = EndianS32_NtoB(0xffffffff);
  597.  
  598.     myMaxMatrix.matrix[0][0] = myMaxMatrix.matrix[0][1] = myMaxMatrix.matrix[0][2] = EndianS32_NtoB(0x7fffffff);
  599.     myMaxMatrix.matrix[1][0] = myMaxMatrix.matrix[1][1] = myMaxMatrix.matrix[1][2] = EndianS32_NtoB(0x7fffffff);
  600.     myMaxMatrix.matrix[2][0] = myMaxMatrix.matrix[2][1] = myMaxMatrix.matrix[2][2] = EndianS32_NtoB(0x7fffffff);
  601.     
  602.     myMinMatrix.matrix[2][1] = EndianS32_NtoB(Long2Fix((1 * kSpriteTrackHeight / 4) - (kPenguinHeight / 2)));
  603.     myMaxMatrix.matrix[2][1] = EndianS32_NtoB(Long2Fix((3 * kSpriteTrackHeight / 4) - (kPenguinHeight / 2)));
  604.  
  605.     SetIdentityMatrix(&myDeltaMatrix);
  606.     myDeltaMatrix.matrix[2][1] = Long2Fix(1);
  607.     
  608.     // change location
  609.     myErr = AddSpriteSetMatrixAction(theContainer, kParentAtomIsContainer, kQTEventIdle, 0, NULL, 0, 0, NULL, &myDeltaMatrix, &myActionAtom);
  610.     if (myErr != noErr)
  611.         goto bail;
  612.  
  613.     myErr = AddActionParameterOptions(theContainer, myActionAtom, 1, myFlags, sizeof(myMinMatrix), &myMinMatrix, sizeof(myMaxMatrix), &myMaxMatrix);
  614.  
  615. bail:
  616.     return(myErr);
  617. }
  618.